#ifndef _BITMAP_STRUCT_H
#define _BITMAP_STRUCT_H
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <math.h>

typedef unsigned char	uchar;
typedef unsigned int 	uint;
typedef signed int		int_32;
typedef unsigned int 	uint_32;
typedef unsigned short 	uint_16;

#pragma pack(1)
typedef struct _BITMAP_STRUCT
{
	// BITMAPFILEHEADER
	uint_16		bfType;
	uint_32 	bfSize;			// file size
	uint_16 	bfReserved1;
	uint_16  	bfReserved2;
	uint_32 	bfOffBits;		// offset to image
	// BITMAPINFOHEADER
	uint_32  	biSize;
	int_32   	biWidth;
	int_32   	biHeight;
	uint_16  	biPlanes;
	uint_16  	biBitCount;
	uint_32  	biCompression;
	uint_32  	biSizeImage;
	int_32   	biXPelsPerMeter;
	int_32   	biYPelsPerMeter;
	uint_32  	biClrUsed;
	uint_32  	biClrImportant; 
	
	uchar*  pColorMatrix;          
}BITMAP_STRUCT, *PBITMAP_STRUCT, *LPBITMAP_STRUCT;
#pragma pack()

class CBitmap : protected BITMAP_STRUCT
{
public:
	static CBitmap* CreateBitmap24(uint_32 width, uint_32 height);
	static CBitmap* CreateBitmap32(uint_32 width, uint_32 height);
	
	uint GetPixel(uint x, uint y);
	void SetPixel(uint x, uint y, uint color);
	void DrawLine(uint x, uint y, int width, int height, uint color=0);
	void DrawRect(uint x, uint y, int width, int height, uint color=0);
	void FillRect(uint x, uint y, int width, int height, uint color);
	
	void*   GetMatrixPtr();
	int     GetImageSize();
	int     GetWidth();
	int     GetHeight();
	int		GetBitCount();
	
	void    SaveFile(char* filename);
	CBitmap(){};
	~CBitmap();
protected:
	CBitmap(uint_32  width, uint_32 height);
//	CBitmap(const CBitmap &){};
//	CBitmap& operator=(const CBitmap &){}; 
};

class CBitmap24 : public CBitmap
{
public:
	CBitmap24(uint_32  width, uint_32 height);       
};

class CBitmap32 : public CBitmap
{
public:
	CBitmap32(uint_32  width, uint_32 height);
};

////////////// CBitmap /////////////////////////
inline CBitmap::CBitmap(uint_32  width, uint_32 height)
{
	memset((BITMAP_STRUCT*)this, 0, sizeof(BITMAP_STRUCT));
	this->bfType    = 0x4D42;
	this->bfOffBits = 0x0036;
	
	this->biSize    = 0x0028;
	this->biPlanes  = 0x001;
	
	this->biWidth    = width;
	this->biHeight   = height;
}

inline CBitmap::~CBitmap()
{
	if(pColorMatrix != NULL)
	delete pColorMatrix;// free(pColorMatrix); 
	pColorMatrix = NULL;
}

inline CBitmap* CBitmap::CreateBitmap24(uint_32  width, uint_32 height)
{
	return new CBitmap24(width, height);
}


inline CBitmap* CBitmap::CreateBitmap32(uint_32  width, uint_32 height)
{
	return new CBitmap32(width, height);
}

inline void* CBitmap::GetMatrixPtr()
{
	return this->pColorMatrix;
}

inline int CBitmap::GetImageSize()
{
	return this->biSizeImage;
}

inline int CBitmap::GetBitCount()
{
	return this->biBitCount;
}

inline int CBitmap::GetWidth()
{
	return this->biWidth;
}

inline int CBitmap::GetHeight()
{
	return this->biHeight;
}

inline uint CBitmap::GetPixel(uint x, uint y)
{
	uint_32 nColor = 0;
	
	if(x >= (uint)biWidth || y >= (uint)biHeight)
		return (uint)nColor;
	
	uint pos = ((biWidth+(biWidth%4+3)/4) * y + x)*(biBitCount/8);
	memcpy(&nColor, this->pColorMatrix+pos, this->biBitCount/8);
	
	return (uint)nColor;
}

inline void CBitmap::SetPixel(uint x, uint y, uint color)
{
	uint_32 nColor = (uint_32)color;
	
	if(x < (uint)biWidth && y < (uint)biHeight)
	{
		uint nTempWidth = (biWidth%4+3) / 4 + biWidth;
		uint pos = (nTempWidth * y + x) * biBitCount/8;  
		memcpy(this->pColorMatrix+pos, &nColor, this->biBitCount/8);
	}
}

inline void CBitmap::DrawLine(uint x, uint y, int width, int height, uint color)
{
    if(width == 0 || height == 0)
        return;
	
    int maxStep = abs(width) > abs(height) ? abs(width) : abs(height);
    for(int idx=0; idx<(int)maxStep; idx++)
        SetPixel(x+(uint)(idx*1.0f*width/maxStep), y+(uint)(idx*1.0f*height/maxStep), color);
}

inline void CBitmap::DrawRect(uint x, uint y, int width, int height, uint color)
{
	for(uint top=x; top!=x+width; top+=width/abs(width))
        SetPixel(top, y, color);
	
    for(uint bottom=x+width; bottom!=x; bottom-=width/abs(width))
        SetPixel(bottom, y+height, color);
	
    for(uint right=y; right != y+height; right+=height/abs(height))
        SetPixel(x+width, right, color);
	
    for(uint left=y+height; left!=y; left-=height/abs(height))
        SetPixel(x, left, color);
}

inline void CBitmap::FillRect(uint x, uint y, int width, int height, uint color)
{
	if(width < 1 || height < 1)
		return;

	int endW = x;
	int endH = y;
	
	if(width > 0)  endW += width;  else x += width;
	if(height > 0) endH += height; else y += height;
	
	int nBit = biBitCount / 8;

	if(endW > this->biWidth)
		endW = this->biWidth;
	if(endH > this->biHeight)
		endH = this->biHeight;

	for(int i=x; i<endW; ++i)
        SetPixel(i,y,color);

	for(int j=y+1; j<endH; ++j)
		memcpy(this->pColorMatrix+(biSizeImage/biHeight)*j+x*nBit, this->pColorMatrix+(biSizeImage/biHeight)*y+x*nBit, (endW-x)*nBit);
}

inline void CBitmap::SaveFile(char* filename)
{
	FILE* file = fopen(filename, "wb");
	if(file == NULL) return;
	
	fwrite(&this->bfType, this->bfOffBits, 1, file);
	fwrite(this->pColorMatrix, this->biSizeImage, 1, file);
	
	fclose(file);
}

/////////// CBitmap24 ///////////////////////////
inline CBitmap24::CBitmap24(uint_32 width, uint_32 height)
: CBitmap(width, height)
{
	this->biBitCount = 24;       
	this->biSizeImage= biBitCount/8 * (width + (width%4+3)/4) * height;
	this->bfSize = this->biSizeImage + this->bfOffBits;

	this->pColorMatrix = (unsigned char*)malloc(biSizeImage);
	memset(pColorMatrix, 0, biSizeImage);
}

/////////// CBitmap32 ///////////////////////////
inline CBitmap32::CBitmap32(uint_32 width, uint_32 height)
: CBitmap(width, height)
{
	this->biBitCount = 32;       
	this->biSizeImage= biBitCount/8 * width * height;
	this->bfSize = this->biSizeImage + this->bfOffBits;

	this->pColorMatrix = new unsigned char[biSizeImage];//(unsigned char*)malloc(biSizeImage);
	memset(pColorMatrix, 0, biSizeImage);
}

#endif
